/*
尽可能还原 Promise 中的每一个 API, 并通过注释的方式描述思路和原理.
*/
const { set, reject } = require('lodash');
const { resolve } = require('./MyPromise');

// 定义promise三种状态：等待、成功、失败
const PENDING = 'pending';
const FULFAILED = 'fulfailled';
const REJECTED = 'rejected';

// Promise就是一个类
class MyPromise {
  // 定义constructor方法，来接受执行器
  constructor(executor) {
    // 通过try catch捕获异常
    try {
      executor(this.resolve, this.reject);
    } catch (e) {
      this.reject(e);
    }
  }

  // 定义promise状态
  status = PENDING;
  // 定义成功和失败后的值
  value = undefined;
  reason = undefined;

  // 定义成功和失败后的回调
  successCallback = [];
  failcallback = [];

  resolve = (value) => {
    // 判断当前状态是否为等待
    // 是则向下执行,否则阻止
    if (this.status != PENDING) return;
    // 将状态更改为成功
    this.status = FULFAILED;
    // 保存成功在之后的值
    this.value = value;
    // 判断回调是否存在，是则调用
    // 通过shift方法，使得回调方法自调用无需传参
    while (this.successCallback.length) this.successCallback.shift()();
  };

  reject = (reason) => {
    if (this.status != PENDING) return;
    // 将状态更改为成功
    this.status = REJECTED;
    // 保存失败后的值
    this.reason = reason;
    // 判断回调是否存在，是则调用
    // 通过shift方法，使得回调方法自调用无需传参
    while (this.failcallback.length) this.failcallback.shift()();
  };

  // 定义then方法，接受成功和失败的回调函数作为参数
  then(successCallback, failcallback) {
    // then方法如果没有回调函数，会默认添加一个回调函数将接受到的value传递下去
    successCallback = successCallback ? successCallback : (value) => value;
    failcallback = failcallback
      ? failcallback
      : (reason) => {
          throw reason;
        };
    // 定义promise2实现then方法链式调用
    let promise2 = new MyPromise((resolve, reject) => {
      // 通过判断当前状态来执行相应的回调
      // 并传递对应的参数值
      if (this.status == FULFAILED) {
        setTimeout(() => {
          // then中捕获异常
          try {
            // 定义x将上一个then的值传递给下一位
            let x = successCallback(this.value);
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      } else if (this.status == REJECTED) {
        setTimeout(() => {
          // then中捕获异常
          try {
            // 定义x将上一个then的值传递给下一位
            let x = failcallback(this.reason);
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      } else {
        // 等待状态
        // 处理等待状态下错误问题
        if (this.status == FULFAILED) {
          setTimeout(() => {
            // then中捕获异常
            try {
              // 定义x将上一个then的值传递给下一位
              let x = successCallback(this.value);
              resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        } else if (this.status.REJECTED) {
          setTimeout(() => {
            // then中捕获异常
            try {
              // 定义x将上一个then的值传递给下一位
              let x = failcallback(this.reason);
              resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        } else {
          // 将成功和失败回调存储起来
          // 链式调用处理，同时处理的每一个回调都进行异常捕获处理
          this.successCallback.push(() => {
            try {
              setTimeout(() => {
                try {
                  let x = successCallback(this.value);
                  // resolve(x);
                  resolvePromise(promise2, x, resolve, reject);
                } catch (e) {
                  reject(e);
                }
              }, 0);
            } catch (e) {
              reject(e);
            }
          });
          this.failcallback.push(() => {
            try {
              setTimeout(() => {
                try {
                  let x = failcallback(this.reason);
                  // resolve(x);
                  resolvePromise(promise2, x, resolve, reject);
                } catch (e) {
                  reject(e);
                }
              }, 0);
            } catch (e) {
              reject(e);
            }
          });
        }
      }
    });

    return promise2;
  }

  // 用来处理当前promise最终结果为失败的情况
  catch(failCallback) {
    return this.then(undefined, failCallback);
  }

  // 无论当前promise的状态最终是成功或者失败，fainally中方法始终会被执行1次,在fainally方法后面链式调用then方法来拿到promise最终返回的结果
  fainally(callback) {
    // 通过then方法得到当前promise状态
    return this.then(
      (value) => {
        callback();
        return value;
      },
      (reason) => {
        callback();
        throw reason;
      }
    );
  }

  // 将给定的值转换成promise对象，promise的返回值就是Promise的对象
  static resolve(value) {
    // 判断是不是promise，是直接返回，不是先转成promise
    if (value instanceof MyPromise) return value;
    return new MyPromise((resolve) => resolve(value));
  }

  // 定义静态的all方法，允许按照异步代码调用顺序得到异步代码
  static all(array) {
    // 定义回调数组
    let result = [];
    // 返回一个promise对象
    return new MyPromise((resolve, reject) => {
      // 定义Index处理异步操作
      let index = 0;
      // 定义方法添加将数组值传入回调数组
      function addData(key, value) {
        // 每次加入回调都计数
        index++;
        result[key] = value;
        // 当所有异步代码都存入异步数组后结束
        if (index === array.length) {
          resolve(result);
        }
      }
      for (let i = 0; i < array.length; i++) {
        let current = array[i];
        if (current instanceof MyPromise) {
          // promise对象
          current.then(
            (value) => {
              return addData(i, value);
            },
            (reason) => {
              reject(reason);
            }
          );
        } else {
          // 普通值
          addData(i, array[i]);
        }
      }
    });
  }
}

/**
 *
 * @param {*} x : 接受值
 */
function resolvePromise(promise2, x, resolve, reject) {
  // 比较promise是否相等
  // 传递promise2,注意需要将promise2编程异步代码
  // 这样所有代码执行完成之后才会执行异步，当所有代码执行完成promise2就能获取到了
  if (x == promise2) {
    reject(new TypeError('Chaining cycle detected for promise #<Promise>'));
  }
  // 判断x的值是普通值还是promise对象
  if (x instanceof MyPromise) {
    // 如果是promise对象 查看promise对象返回的结果
    // 再根据promise对象返回的结果决定调用resolve还是调用reject
    x.then(resolve, reject);
  } else {
    // 如果是普通值 直接调用resolve
    resolve(x);
  }
}
// 最后导出MyPromise
module.exports = MyPromise;
